package br.com.ac.fatureweb.utilitary;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.annotation.security.RolesAllowed;
import javax.ejb.EJBAccessException;
import javax.ejb.EJBTransactionRolledbackException;
import javax.faces.component.UIComponent;
import javax.faces.event.ActionEvent;
import javax.faces.event.AjaxBehaviorEvent;
import javax.persistence.PersistenceException;
import javax.validation.ConstraintViolationException;

import org.primefaces.component.datatable.DataTable;

/**
 * 
 * @author aldivone
 *
 * @param <Entity>
 * @param <NgcDefault>
 */
public abstract class CrudDefaultBean<Entity, NgcDefault extends NgcDefaultBean<Entity>>
		implements Serializable {

	private static final long serialVersionUID = -8067835222071047020L;

	protected final String MESSAGE_GENERIC = "Ops! não conseguimos processar sua solicitação. Favor contactar o administrador do sistema.";

	protected final String MESSAGE_ACCESS_DATABASE = "Ops! estamos com problema para executar sua solicitação na base de dados. Favor contactar o administrador do sistema.";

	protected final String CURRENT_STATE = "currentState";

	public static final String MESSAGE_CONSTRAINT_VIOLATION = "Exclusão não permitida, registro referênciado em outro cadastro do sistema.";

	public static final String ENTITY = "entityModel";

	public static final String PARAMETERS_SEARCH = "parametersSearch";

	public static final String RESULT_SEARCH = "resultadoConsulta";

	private NgcDefault service;

	private final Map<String, Order> Order = new LinkedHashMap<String, Order>();

	private UIComponent pnlEdition;

	private DataTable tblData;

	private Entity entity;

	private Entity parametersSearch;

	private EnumStatusManaged currentState;

	protected PageDataModel<Entity> resultSearch;

	private Collection<Object> selection = new ArrayList<Object>();

	/**
	 * 
	 */
	public CrudDefaultBean() {
		initializeEntityCrud();
	}

	/**
	 * 
	 */
	public void initializeEntityCrud() {
		if (getEntity() == null) {
			setEntity(getNewInstance());
		}
		FacesUtils.initializeEntity(getEntity());

		if (getParametersSearch() == null) {
			setParametersSearch(getNewInstance());
		}
		FacesUtils.initializeEntity(getParametersSearch());
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_INCLUIR)
	public void preparePersist(final ActionEvent event) {
		try {
			tableSelectionClean();

			FacesUtils.cleanSubmittedValues(pnlEdition);

			setEntity(getNewInstance());

			FacesUtils.initializeEntity(getEntity());

			// Alterna os panels necess�riosrios para mostrar o conte�do da
			// inclus�o
			setCurrentState(EnumStatusManaged.INCLUIR);
		} catch (final ConstraintViolationException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_CONSTRAINT_VIOLATION);
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.createMessageErrorAccess(e);
		} catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final PersistenceException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final RuntimeException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		} catch (final Exception e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		}
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_ALTERAR)
	public void prepareMerge(final ActionEvent event) {
		try {
			Entity selectedRowData = getSelectedTableRow();

			if (selectedRowData != null
					&& FacesUtils.getPropertyIdValue(selectedRowData) != null) {

				setEntity(selectedRowData);

				FacesUtils.initializeEntity(getEntity());

				setCurrentState(EnumStatusManaged.ALTERAR);

				executeAfterShowWindowMerge();
			} else {
				FacesUtils.addMessageWarning(FacesUtils.MESSAGE_NO_SELECTION);
			}
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.createMessageErrorAccess(e);
		} catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final PersistenceException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final RuntimeException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		} catch (final Exception e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		}
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_EXCLUIR)
	public void prepareRemove(final ActionEvent event) {
		try {
			Entity selectedRowData = getSelectedTableRow();

			if (selectedRowData != null
					&& FacesUtils.getPropertyIdValue(selectedRowData) != null) {
				setEntity(selectedRowData);

				FacesUtils.initializeEntity(getEntity());
				// TODO se necessário dar um refresh se for muito usado.

				setCurrentState(EnumStatusManaged.EXCLUIR);
			} else {
				FacesUtils.addMessageWarning(FacesUtils.MESSAGE_NO_SELECTION);
			}
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.createMessageErrorAccess(e);
		} catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final PersistenceException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final RuntimeException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		} catch (final Exception e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		}
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_INCLUIR)
	public void persist(final ActionEvent event) {
		try {
			if (executeValidationPrePersist()) {
				FacesUtils.setPropertyIdValue(entity, null);
				final Entity entity = getEntity();

				runBeforeCallingService();

				FacesUtils.removeTransients(entity);

				setEntity(service.persist(entity));

				runAfterCallingService();

				search(event);

				cleanListInRequest();
			}
		} catch (final ConstraintViolationException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_CONSTRAINT_VIOLATION);
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			FacesUtils.createMessageErrorAccess(e);
			initializeEntityCrud();
			FacesUtils.setPropertyIdValue(getEntity(), null);
		} catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.setPropertyIdValue(getEntity(), null);
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final PersistenceException e) {
			e.printStackTrace();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
			initializeEntityCrud();
			FacesUtils.setPropertyIdValue(getEntity(), null);
		} catch (final RuntimeException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		} catch (final Exception e) {
			e.printStackTrace();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
			initializeEntityCrud();
			FacesUtils.setPropertyIdValue(getEntity(), null);
		}
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_ALTERAR)
	public void merge(final ActionEvent event) {

		try {
			if (executeValidationPreMerge()) {
				final Entity entity = getEntity();

				runBeforeCallingService();

				FacesUtils.removeTransients(entity);

				setEntity(service.merge(entity));

				runAfterCallingService();

				search(event);

				cleanListInRequest();
			}
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			FacesUtils.createMessageErrorAccess(e);
			initializeEntityCrud();
		}

		catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
			initializeEntityCrud();
		} catch (final PersistenceException e) {
			e.printStackTrace();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
			initializeEntityCrud();
		} catch (final RuntimeException e) {
			e.printStackTrace();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
			initializeEntityCrud();
			FacesUtils.setPropertyIdValue(getEntity(), null);
		} catch (final Exception e) {
			e.printStackTrace();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
			initializeEntityCrud();
			FacesUtils.setPropertyIdValue(getEntity(), null);
		}
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_EXCLUIR)
	public void remove(final ActionEvent event) {
		final Long codEntity = FacesUtils.getPropertyIdValue(getEntity());

		try {
			if (executeValidationPreRemove()) {
				runBeforeCallingService();

				service.remove(codEntity);

				runAfterCallingService();

				search(event);

				setCurrentState(EnumStatusManaged.CONSULTAR);
			}
		} catch (final ConstraintViolationException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_CONSTRAINT_VIOLATION);
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.createMessageErrorAccess(e);
		} catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final PersistenceException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final RuntimeException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		} catch (final Exception e) {
			e.printStackTrace();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
			initializeEntityCrud();
		}
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_CONSULTAR)
	public void view(final ActionEvent event) {
		try {
			Entity selectedRowData = getSelectedTableRow();

			if (selectedRowData != null
					&& FacesUtils.getPropertyIdValue(selectedRowData) != null) {

				setEntity(selectedRowData);

				setCurrentState(EnumStatusManaged.VISUALIZAR);

				runAfterCallingService();

				FacesUtils.initializeEntity(getEntity());
			} else {
				FacesUtils.addMessageWarning(FacesUtils.MESSAGE_NO_SELECTION);
			}
		}

		catch (final ConstraintViolationException e) {
			e.printStackTrace();
			FacesUtils.addMessageError(MESSAGE_CONSTRAINT_VIOLATION);
			initializeEntityCrud();
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			FacesUtils.createMessageErrorAccess(e);
			initializeEntityCrud();
		} catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final PersistenceException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final RuntimeException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		} catch (final Exception e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		}
	}

	/**
	 * 
	 * @param event
	 */
	@RolesAllowed(Constants.PERMISSAO_CONSULTAR)
	public void search(final ActionEvent event) {
		try {
			if (tblData != null) {
				final Entity parametersSearch = getParametersSearch();

				Map<String, Object> mapParam = new HashMap<String, Object>();

				mapParam = setParametersSearch(mapParam, parametersSearch);

				final PageDataModel<Entity> result = new PageDataModel<Entity>(
						tblData.getRows(), service, mapParam, Order);

				setResultSearch(result);

				final int rowCount = result.getRowCount();

				tblData.setRendered(rowCount > 0);

				if (rowCount <= 0) {
					FacesUtils.addMessageWarning(FacesUtils.MESSAGE_NO_RESULT);
				}

				setCurrentState(EnumStatusManaged.CONSULTAR);
			}
		} catch (final EJBAccessException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.createMessageErrorAccess(e);
		}

		catch (final EJBTransactionRolledbackException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final PersistenceException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_ACCESS_DATABASE);
		} catch (final RuntimeException e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		} catch (final Exception e) {
			e.printStackTrace();
			initializeEntityCrud();
			FacesUtils.addMessageError(MESSAGE_GENERIC);
		}
	}

	/**
	 * 
	 * @param event
	 */
	public void cancel(final ActionEvent event) {
		cleanListInRequest();

		if (getResultSearch() != null && getResultSearch().getRowData() != null) {
			search(event);
		}

		setCurrentState(EnumStatusManaged.CONSULTAR);

		FacesUtils.cleanSubmittedValues(pnlEdition);
	}

	/**
	 * 
	 * @param event
	 */
	public void clean(final ActionEvent event) {
		setParametersSearch(getNewInstance());
		cleanListInRequest();
		setCurrentState(EnumStatusManaged.CONSULTAR);
		FacesUtils.initializeEntity(getParametersSearch());
	}

	/**
	 * 
	 * @param event
	 */
	public void back(ActionEvent event) {
		setCurrentState(EnumStatusManaged.CONSULTAR);
		FacesUtils.initializeEntity(getParametersSearch());
	}

	/**
	 * 
	 * @param event
	 */
	public void print(ActionEvent event) {

	}

	/**
	 * 
	 * @return
	 */
	protected boolean executeValidationPrePersist() {
		return Boolean.TRUE;
	}

	/**
	 * 
	 * @return
	 */
	protected boolean executeValidationPreMerge() {
		return Boolean.TRUE;
	}

	/**
	 * 
	 * @return
	 */
	protected boolean executeValidationPreRemove() {
		return Boolean.TRUE;
	}

	/**
	 * Execute action before calling service, check the status of the action.
	 */
	protected void runBeforeCallingService() {
	}

	/**
	 * Execute action after calling service, check the status of the action.
	 */
	protected void runAfterCallingService() {
	}

	/**
	 * 
	 */
	protected void cleanListInRequest() {
	}

	/**
	 * 
	 * @param m
	 */
	public void addAllOrder(final Map<? extends String, ? extends Order> m) {
		Order.putAll(m);
	}

	public Order addOrder(final String key, final Order value) {
		return Order.put(key, value);
	}

	/**
	 * 
	 * @param idComp
	 */
	protected void cleanSubmittedValues(final String idComp) {
		FacesUtils.cleanSubmittedValues(idComp);
	}

	/**
	 * 
	 * @param component
	 */
	protected void cleanSubmittedValues(final UIComponent component) {
		FacesUtils.cleanSubmittedValues(component);
	}

	/**
	 * 
	 * @param mapParam
	 * @param parametrosBusca
	 * @return
	 */
	public Map<String, Object> setParametersSearch(
			Map<String, Object> mapParam, Entity parametrosBusca) {
		try {
			FacesUtils.removeTransients(parametrosBusca);
			mapParam = FacesUtils.convertObjectToMap(parametrosBusca);

			return mapParam;
		} catch (IllegalAccessException e) {
			FacesUtils.addMessageError(MESSAGE_GENERIC);
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			FacesUtils.addMessageError(MESSAGE_GENERIC);
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			FacesUtils.addMessageError(MESSAGE_GENERIC);
			e.printStackTrace();
		}
		return mapParam;
	}

	/**
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Class<Entity> getEntityClass() {
		return (Class<Entity>) ((java.lang.reflect.ParameterizedType) this
				.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public NgcDefault getService() {
		return service;
	}

	/**
	 * 
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected Entity getNewInstance() {
		try {
			return (Entity) ((Class) ((java.lang.reflect.ParameterizedType) this
					.getClass().getGenericSuperclass())
					.getActualTypeArguments()[0]).newInstance();
		} catch (final Exception e) {
			System.out
					.println("Os objetos não puderam ser instanciados corretamente.");
			return null;
		}
	}

	/**
	 * 
	 * @param key
	 * @return
	 */
	public Order getOrder(final Object key) {
		return Order.get(key);
	}

	/**
	 * 
	 * @return
	 */
	public UIComponent getPnlEdition() {
		return pnlEdition;
	}

	/**
	 * 
	 * @return
	 */
	public DataTable getTblData() {
		return tblData;
	}

	/**
	 * 
	 * @param nameAttribute
	 * @return
	 */
	protected String locateValueToPropertyInResource(final String nameAttribute) {
		final String nomePacote = getClass().getPackage().getName();

		final String nomeArquivo = getClass().getSimpleName();

		final String primeiraLetra = nomeArquivo.substring(0, 1);

		final String novoNomeArquivo = primeiraLetra.toLowerCase()
				+ nomeArquivo.substring(1, nomeArquivo.lastIndexOf("Bean"));

		final String nomeArquivoMessage = nomePacote + "." + novoNomeArquivo;

		String retorno = null;

		try {
			final ResourceBundle bundle = ResourceBundle
					.getBundle(nomeArquivoMessage);

			if (bundle != null) {
				retorno = bundle.getString(nameAttribute);
			}

			return retorno;
		} catch (final MissingResourceException e) {
			e.printStackTrace();
		} catch (final Exception e) {
			e.printStackTrace();
		}

		return retorno;
	}

	/**
	 * 
	 */
	protected void executeAfterShowWindowMerge() {

	}

	/**
	 * 
	 * @param ngcService
	 */
	public void setService(final NgcDefault ngcService) {
		this.service = ngcService;
	}

	/**
	 * 
	 * @param pnlEdition
	 */
	public void setPnlEdition(final UIComponent pnlEdition) {
		this.pnlEdition = pnlEdition;
	}

	public void setTblData(final DataTable tblData) {
		this.tblData = tblData;
	}

	/**
	 * 
	 */
	private void tableSelectionClean() {
		selection = null;
	}

	/**
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Entity getSelectedTableRow() {
		Entity selectedRowData = (Entity) getTblData().getRowData();

		return selectedRowData;
	}

	/**
	 * 
	 * @return
	 */
	public Map<String, Order> getOrder() {
		return Order;
	}

	public Entity getEntity() {
		return entity;
	}

	/**
	 * 
	 * @param entity
	 */
	public void setEntity(Entity entity) {
		this.entity = entity;
	}

	/**
	 * 
	 * @return
	 */
	public Entity getParametersSearch() {
		return parametersSearch;
	}

	/**
	 * 
	 * @param parametersSearch
	 */
	public void setParametersSearch(Entity parametersSearch) {
		this.parametersSearch = parametersSearch;
	}

	/**
	 * 
	 * @return
	 */
	public EnumStatusManaged getCurrentState() {
		return currentState;
	}

	/**
	 * 
	 * @param currentState
	 */
	public void setCurrentState(EnumStatusManaged currentState) {
		this.currentState = currentState;
	}

	/**
	 * 
	 * @return
	 */
	public String getState() {
		if (currentState != null) {
			return currentState.getAcao();
		}

		return null;
	}

	/**
	 * 
	 * @return
	 */
	public PageDataModel<Entity> getResultSearch() {
		return resultSearch;
	}

	/**
	 * 
	 * @param resultSearch
	 */
	public void setResultSearch(PageDataModel<Entity> resultSearch) {
		this.resultSearch = resultSearch;
	}

	/**
	 * 
	 * @param event
	 */
	@SuppressWarnings("unchecked")
	public void selectionRow(AjaxBehaviorEvent event) {
		DataTable dataTable = (DataTable) event.getComponent();

		Object originalKey = dataTable.getRowKey();

		for (Object selectionKey : selection) {
			dataTable.setRowKey(selectionKey);

			if (dataTable.isRowAvailable()) {
				setEntity((Entity) dataTable.getRowData());
			}
		}
		dataTable.setRowKey(originalKey);
	}

	/**
	 * 
	 * @return
	 */
	public Collection<Object> getSelection() {
		return selection;
	}

	/**
	 * 
	 * @param selection
	 */
	public void setSelection(Collection<Object> selection) {
		this.selection = selection;
	}

}